#ifndef SOCKET_H
#define SOCKET_H
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdint.h>
#include <netdb.h>
#include <netinet/in.h>
#endif

#ifndef VECTOR_H
#define VECTOR_H
#include <vector>
#endif

#ifndef MAP_H
#define MAP_H
#include <map>
#endif

#define BACKLOG_MAX 10

#include <string.h>
#include <string>
#include <stdio.h>

#ifndef CACHE_H
#define CACHE_H
#include "../Cache/HTTP.h"
#endif

#ifndef ERROR_H
#define ERROR_H
#include "../Errors/errors.h"
#endif

#ifndef PROXYCONN_H
#define PROXYCONN_H
#include "ProxyConnection.h"
#endif

using namespace std;


class ProxyServer {
	sockaddr_in host_to_addr(std::string _host, unsigned short _port);
	struct sockaddr_in myaddr;
	unsigned short port;

public:
	int incoming_sock;
	std::map<int, HTTPRequest*> cache;
	std::map<int, HTTPRequest*>::iterator cache_it;
	struct timeval tv;
	int result;
	int maxfdp;
	int conn_number;

	ProxyServer(std::string _ip, unsigned short _port)
	:port(_port){
		port = _port;
		// Get host information
		myaddr = host_to_addr(_ip, _port);
		// Initialize socket
		//inet_aton(_host, &myaddr.sin_addr);
		incoming_sock = socket(AF_INET, SOCK_STREAM, 0);
		result = bind(incoming_sock,
				(struct sockaddr*)&myaddr,
				sizeof(struct sockaddr_in));
		if (result < 0)
		{
			std::string err_msg = "";
			if(errno == EACCES) {
				err_msg = "bind: permission denied.";
			}
			else if(EINVAL) {
				err_msg = "Socket is already bound to an address.";
			}
			else if(ENAMETOOLONG) {
				err_msg = "Address is too long.";
			}
			else {
				err_msg = "Could not bind socket to port";
			}
			printf("Error: %s\n", err_msg.c_str());
			incoming_sock = null;
		}
		else {
			printf("listening: %d\n", incoming_sock);
			result = listen(incoming_sock, BACKLOG_MAX);
			if(result < 0) {
				std::string err_msg = "";
				if(errno == EADDRINUSE) {
					err_msg = "Port is already in use.";
				}
				else {
					err_msg = "Could not listen on port.";
				}
				printf("Error: %s\n", err_msg.c_str());
				incoming_sock = null;
			}
		}
	}

	void stop_server();
	void send_client(int _sock, std::vector<char> data);
	int search_cache(string _url);
	void handle_server_connection(ProxyConnection conn);
	ProxyConnection receive_incoming_connection();
	
};

